<script setup lang="ts">
import type { Key } from 'ant-design-vue/es/vc-tree/interface';

import type { Nullable, Recordable } from '@vben/types';

import type { DefResourceModel } from '#/api';
import type {
  ContextMenuItem,
  TreeActionItem,
  TreeActionType,
  TreeItem,
} from '#/components/tree';

import { h, onMounted, reactive, ref, unref } from 'vue';
import { useRouter } from 'vue-router';

import { PermModeEnum } from '@vben/access';

import {
  DeleteOutlined,
  DragOutlined,
  EditOutlined,
  PlusSquareOutlined,
} from '@ant-design/icons-vue';
import { Button, Select, Tag } from 'ant-design-vue';

import { DefApplicationApi, DefResourceApi } from '#/api';
import { BasicTree } from '#/components/tree';
import { DictEnum } from '#/enums/commonEnum';
import { PermCode } from '#/enums/perm';
import { ResourceTypeEnum } from '#/enums/system';
import { useMessage } from '#/hooks/web/useMessage';
import { $t } from '#/locales';
import { useDictStore } from '#/store';
import { eachTree, findNodeByKey } from '#/utils/helper/treeHelper';

import MoveModal from './move.vue';

defineOptions({
  name: 'DefResourceTree',
  inheritAttrs: false,
});

const emit = defineEmits<Emits>();

interface Emits {
  (e: 'select', parent: TreeItem, current: TreeItem): void;
  (e: 'add', parent: TreeItem, current: any): void;
  (e: 'edit', parent: TreeItem, current: TreeItem): void;
  (e: 'change', id: string, name: string): void;
}

const { createMessage, createConfirm } = useMessage();
const { currentRoute } = useRouter();

const treeRef = ref<Nullable<TreeActionType>>(null);
const treeData = ref<TreeItem[]>([]);
const treeLoading = ref<boolean>(false);
const data = reactive<Recordable>({
  applicationList: [],
  appDisabled: false,
});
const applicationRef = reactive({ value: '', label: '' });
const permCode = PermCode.devOperation.application.defResource;
const resourcePermCode = PermCode.devOperation.application.defApplication;
const moveRef = ref();
const dictStore = useDictStore();
function getTree() {
  const treeR = unref(treeRef);
  if (!treeR) {
    throw new Error('树结构加载失败,请刷新页面');
  }
  return treeR;
}

onMounted(async () => {
  const params = currentRoute.value?.params;
  applicationRef.value = params?.id as string;
  const applications = await DefApplicationApi.query({
    id: params?.id as string,
  });
  data.appDisabled = Boolean(params?.id);

  data.applicationList = applications.map((item) => ({
    label: item.name,
    value: item.id,
    key: item.id,
  }));
  if (applications && applications[0]) {
    applicationRef.value = applications[0].id;
    applicationRef.label = applications[0].name;
    await fetch(applicationRef.value);
  }
});

// 加载数据
async function fetch(applicationId?: string) {
  try {
    treeLoading.value = true;

    const appId = applicationId || applicationRef.value;
    if (appId) {
      treeData.value = (await DefResourceApi.tree({
        applicationId: appId,
      })) as unknown as TreeItem[];

      eachTree(
        treeData.value,
        (item, parent) => {
          item.key = item.id;
          item.title = item.name;
          item.keyLinks = [...(parent.keyLinks || []), item.id];
          item.slots = { titleBefore: 'titleBefore' };
          return item;
        },
        {},
      );

      setTimeout(() => {
        getTree().setCheckedKeys([]);
      }, 0);
    } else {
      createMessage.warning('请先选择应用');
    }
  } finally {
    treeLoading.value = false;
  }
}

// 选择节点
function handleSelect(keys: Key[]) {
  if (keys[0]) {
    const node = findNodeByKey(keys[0], treeData.value);
    const parent = findNodeByKey(node?.parentId, treeData.value);

    emit('select', parent, node);
  }
}

// 移动节点
function handleMove(current = {}) {
  moveRef.value.open();
  moveRef.value.setData({
    applicationId: applicationRef.value,
    current,
  });
}

// 悬停图标
const actionList: TreeActionItem[] = [
  {
    auth: [permCode.move, resourcePermCode.resourceMove],
    authMode: PermModeEnum.HasAny,
    show: (node) => {
      return [ResourceTypeEnum.BUTTON, ResourceTypeEnum.MENU].includes(
        node.resourceType,
      );
    },
    render: (node) => {
      return h(DragOutlined, {
        class: 'ml-2',
        title: $t('common.title.move'),
        onClick: (e: Event) => {
          e?.stopPropagation();
          e?.preventDefault();
          getTree().setSelectedKeys([]);
          handleMove(findNodeByKey(node.id, treeData.value));
        },
      });
    },
  },
  {
    auth: [permCode.add, resourcePermCode.resourceAdd],
    authMode: PermModeEnum.HasAny,
    show: (node) => {
      return [ResourceTypeEnum.BUTTON, ResourceTypeEnum.MENU].includes(
        node.resourceType,
      );
    },
    render: (node) => {
      return h(PlusSquareOutlined, {
        class: 'ml-2',
        title: $t('common.title.add'),
        onClick: (e: Event) => {
          e?.stopPropagation();
          e?.preventDefault();
          getTree().setSelectedKeys([]);
          emit('add', findNodeByKey(node.id as Key, treeData.value), {
            applicationId: applicationRef.value,
            applicationName: applicationRef.label,
          });
        },
      });
    },
  },
  {
    auth: [permCode.edit, resourcePermCode.resourceEdit],
    authMode: PermModeEnum.HasAny,
    render: (node) => {
      return h(EditOutlined, {
        class: 'ml-2',
        title: $t('common.title.edit'),
        onClick: (e: Event) => {
          e?.stopPropagation();
          e?.preventDefault();
          getTree().setSelectedKeys([]);
          const current = findNodeByKey(node?.id as Key, treeData.value);
          const parent = findNodeByKey(node?.parentId as Key, treeData.value);
          current.applicationName = applicationRef.label;
          current.applicationId = applicationRef.value;
          emit('edit', parent, current);
        },
      });
    },
  },
  {
    auth: [permCode.delete, resourcePermCode.resourceDelete],
    authMode: PermModeEnum.HasAny,
    render: (node) => {
      return h(DeleteOutlined, {
        class: 'ml-2',
        title: $t('common.title.delete'),
        style: { color: '#ED6F6F' },
        onClick: (e: Event) => {
          e?.stopPropagation();
          e?.preventDefault();
          getTree().setSelectedKeys([]);
          batchDelete([node.id as string]);
        },
      });
    },
  },
];

// 右键菜单
const getRightMenuList = (node: any): ContextMenuItem[] => {
  return [
    {
      label: $t('common.title.add'),
      auth: [permCode.add, resourcePermCode.resourceAdd],
      authMode: PermModeEnum.HasAny,
      handler: () => {
        getTree().setSelectedKeys([]);
        emit('add', findNodeByKey(node.id as Key, treeData.value), {
          applicationId: applicationRef.value,
          applicationName: applicationRef.label,
        });
      },
      icon: 'ant-design:plus-outlined',
    },
    {
      auth: [permCode.edit, resourcePermCode.resourceEdit],
      authMode: PermModeEnum.HasAny,
      label: $t('common.title.edit'),
      handler: () => {
        getTree().setSelectedKeys([]);
        const current = findNodeByKey(node?.id as Key, treeData.value);
        const parent = findNodeByKey(node?.parentId as Key, treeData.value);
        current.applicationName = applicationRef.label;
        current.applicationId = applicationRef.value;
        emit('edit', parent, current);
      },
      icon: 'ant-design:edit-outlined',
    },
    {
      auth: [permCode.delete, resourcePermCode.resourceDelete],
      authMode: PermModeEnum.HasAny,
      label: $t('common.title.delete'),
      handler: () => {
        getTree().setSelectedKeys([]);
        batchDelete([node?.id as string]);
      },
      icon: 'ant-design:delete-outlined',
    },
  ];
};

// 点击树外面的 新增
function handleAdd() {
  const parent = findNodeByKey('0', treeData.value) as TreeItem;
  const current = {
    applicationId: applicationRef.value,
    applicationName: applicationRef.label,
  };
  getTree().setSelectedKeys([]);
  emit('add', parent, current);
}

// 执行批量删除
async function batchDelete(ids: string[]) {
  createConfirm({
    iconType: 'warning',
    content: '选中节点及其子结点将被永久删除, 是否确定删除？',
    onOk: async () => {
      try {
        await DefResourceApi.remove(ids);
        await fetch();
        createMessage.success($t('common.tips.deleteSuccess'));
      } catch {}
    },
  });
}

// 点击树外面的 批量删除
function handleBatchDelete() {
  const checkedKeys = getTree().getCheckedKeys();
  const checked = getTree().getCheckStrictly()
    ? (checkedKeys as any).checked
    : checkedKeys;

  if (!checked || checked.length <= 0) {
    createMessage.warn($t('common.tips.pleaseSelectTheData'));
    return;
  }
  batchDelete(checked as string[]);
}

async function handleChange({ value, label }: any) {
  applicationRef.label = label;
  applicationRef.value = value;
  await fetch(value);
  emit('change', value, applicationRef.label);
}

async function handleSuccess() {
  await fetch();
}

const getColor = (item: DefResourceModel.DefResourceResultVO) => {
  if (item?.isHidden) {
    return 'default';
  }
  return dictStore.getDictItem(DictEnum.ResourceTypeEnum, item.resourceType)
    ?.propType;
};

defineExpose({ fetch });
</script>
<template>
  <div class="overflow-hidden">
    <div class="mb-2 mt-2">
      <Select
        v-model:value="applicationRef"
        :disabled="data.appDisabled"
        :options="data.applicationList"
        label-in-value
        placeholder="选择应用"
        show-search
        style="width: 100%; margin-bottom: 1rem"
        @change="handleChange"
      />

      <Button
        class="mr-2"
        @click="handleAdd()"
        v-hasAnyPermission="[permCode.add, resourcePermCode.resourceAdd]"
      >
        {{ $t('common.title.addRoot') }}
      </Button>
      <Button
        class="mr-2"
        v-hasAnyPermission="[permCode.delete, resourcePermCode.resourceDelete]"
        @click="handleBatchDelete()"
      >
        {{ $t('common.title.delete') }}
      </Button>
      <Button class="mr-2" @click="fetch()">
        {{ $t('common.redo') }}
      </Button>
    </div>
    <BasicTree
      ref="treeRef"
      :field-names="{ key: 'id', title: 'name' }"
      :action-list="actionList"
      :before-right-click="getRightMenuList"
      :click-row-to-expand="false"
      :loading="treeLoading"
      checkable
      :title="$t('devOperation.application.defResource.table.title')"
      :tree-data="treeData"
      check-strictly
      highlight
      search
      toolbar
      @select="handleSelect"
    >
      <template #titleBefore="item">
        <template v-if="item.echoMap?.resourceType">
          <Tag class="ml-2" :color="getColor(item)" :title="item.treePath">
            {{ item.echoMap?.resourceType }}
          </Tag>
        </template>
      </template>
    </BasicTree>

    <MoveModal ref="moveRef" @success="handleSuccess" />
  </div>
</template>
